// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


// Debug flags; should all be false on final release build
#define SHOW_FPS           false
#define SKIP_MENUS         false
#define SKIP_TWOPLAYER     false
#define SKIP_REVERSED      false
#define GODMODE            false
#define INFINITEMODE       false
#define ALWAYSSTAR         false
#define CLEARDEFAULTS      false
#define ROBOTUNLOCKED      false
#define ROBOTMAXED         false
#define ALWAYSTUTORIAL     false
#define FAKECHAINSTATS     false
#define HANDICAPVSAI       false
#define REACHED500         false
#define ROBOTMAX2          false
#define FORCEMEMORYWARNING false
#define LINESREACHED10     false
#define NORAISEHINT        false
#define STEELBOT6          false

// Feature flags
#define BGSQUARES false
#define MUSIC true
#define USE_OPENFEINT false
#define PUZZLEMODE_AVAILABLE false
#define DEPTHLABEL false
#define AUTOROTATEANIMATION true
#define THREEARROWS false
#define CANCELTRASH false
#define BLOCKGLOW false
#define FLEXCURSOR true
#define TRASHBURST true
#define COCOSDENSHION true

#import "cocos2d.h"

#if !COCOSDENSHION
#import <AVFoundation/AVFoundation.h>
#define MAX_SOUNDS 10
#endif

// Singletons
@class SceneManager;
extern CCTexture2D* emptytex;
extern CCTexture2D* blocktex[3][3];
extern CCAnimation* heartanims[3][3];
extern SceneManager* scene;
extern const char* charnames[];

int myrandom(unsigned int range);

#define ARRAYSIZE(a) (sizeof(a)/sizeof((a)[0]))

// Timing constants
#define FRAMETIME 0 //0.01 // interval between gameLoop calls
#define FALLDELAY 0.10  // time a block hangs in the air after the block under it disappears
#define SPAWNDELAY 0.25  // time a block hangs in the air when put by player (time of throwing animation)
#define TUTORIALDELAY 1.6
#define RISEDELAY 0.20  // time taken up by one level of raising animation
#define OPENINGRISEDELAY 0.10  // same, but for opening raise before the game starts
#define COUNTDOWNSOUNDDELAY 0.60  // time before playing countdown sound
#define FALLSPEED 20.0  // grid units per second
#define STARTRAISEHEIGHT 10  // starting height of stack when game opens
#define TRASHEMITDELAY 0.1 // time between sending several combo/chain icons
#define TRASHMOVETIME 0.3 // time to send each trash icon
#define STARTDELAY 1.5 // Time to display "Ready"
#define ENDDELAY 4.0 // Time to keep playing after winning
#define CARDFLIPTIME 0.25 // Time for half of card flip animation

// Constants with difficulty impact
#define MEDIUMMELTTIME 1.2
#define MELTTIME (g_->difficulty == MEDIUM ? MEDIUMMELTTIME : (g_->difficulty == EASY ? 1.4 : 0.9)) // seconds for block to blink before vanishing
#define GENEROUSTRASHMELTTIME 0.3 // slack time for trash falling after match
#define DEATHCOUNTTIME (g_->difficulty == MEDIUM ? 1.7 : (g_->difficulty == EASY ? 2.0 : 1.3))  // time per unit of countdown
#define HEARTS_FOR_TOKEN_TRASH (g_->difficulty == MEDIUM ? 8 : (g_->difficulty == EASY ? 4 : 10000)) // number of hearts to clear to get an attack despite not chaining/comboing

// Block types
enum BlockColor {
	EMPTY = 0,
	TRASH = 1,
	RED = 2,
	GREEN = 3,
	BLUE = 4,
};
typedef enum BlockColor BlockColor;

enum BlockShape {
	LEFT = 8,
	UP = 16,
	RIGHT = 32,
	DOWN = 64,
	LEFTRIGHT = 40,
	UPDOWN = 80,
	
	TRASHx0 = 128,
	TRASHx1 = 256,
	TRASHx2 = 512,
	TRASHx3 = 1024,
};
typedef enum BlockShape BlockShape;

enum Mascot {
	KEITH = 1,
	BEAR = 2,
	ROBOT = 3,

	NUM_MASCOTS = ROBOT,
};
typedef enum Mascot Mascot;

enum Difficulty {
	EASY = 0,
	MEDIUM = 1,
	HARD = 2,

	NUM_DIFFICULTY,
};
typedef enum Difficulty Difficulty;


// MainMenu relative depths
#define MENUBGMIDDLE_Z 10
#define TIP_Z 15
#define MENUBGRIGHT_Z 20
#define MENUBGLEFTTOP_Z 20
#define MENUBUTTON_Z 21
#define ROBOTARMS_Z 25
#define MENUBGLEFTBOTTOM_Z 30
#define MENUMASCOT_Z 35
#define FINALCHARS_Z 36
#define TITLEBEAM_Z 37
#define MENUFINGERS_Z 40
#define TIPARROWS_Z 50

// Blocksdisplay relative depths
#define BLOCK_Z 50
#define GOALLINE_Z 75
#define CURSOR_Z 150
#define SCROLLDARKENER_Z 300

// Puzzlestack relative depths 
#define BACKGROUNDSLICE_Z 20
#define SIDE_BACKGROUND_Z 25
#define SIDE_ELEMENT_Z 51
#define TRASHICONDISPLAY_Z 98
#define BACKGROUND_Z 100
#define BLOCKSDISPLAY_Z 101
#define CHAIN_Z 400
#define FOREGROUND_Z 450
#define STATS_Z 460
#define MASCOT_Z 475
#define CONFETTI_Z 499
#define WINLOSE_Z 500
#define DANGERGLOW_Z 600
#define DANGERSIGN_Z 650

// Lazily assume no player will ever do more than this many chains for stats recording purposes.
#define MAX_CHAIN 200

// Descriptor of elements of a game board that carry over across multiple games.  Mostly
// positioning of various graphical objects (created at launch and then referenced), but also a few things mutated
// over time such as number of wins.
typedef struct GridConfig_ {
	int playerno;

	int tilesize_x, tilesize_y;
	int numtiles_x, numtiles_y;
	int px_left, px_bottom;
	int playerareasize_x, playerareasize_y;
	int playerareastart_x;

	int sidebar_x;

	// Relative to sidebar_x
	CGPoint dangersignpos;
	CGPoint mascotpos, nextlabelpos;
	CGPoint timerpos, pausepos, helppos, menupos;
	// Relative to px_left
	CGPoint winlabelpos, winstartpos;

	bool upsidedown;  // causes touches to be interpreted upside down
	bool mirrored;  // causes the mascot to be drawn facing right
	int offsetx;

	// Mutable elements.  Should all be cleared in ClearGridMutable

	// Records over the current sequence of games
	int wins; // counting wins for this side
	bool is_ai;
	int ailevel; // counting AI level for this side
	Mascot mascot;
	Difficulty difficulty;
	bool is_ready; // ready to start pressed?
	bool practice; // practice mode in singleplayer
	bool puzzle; // puzzle mode in singleplayer
} GridConfig;

void ClearGridMutable(GridConfig* g);

// Block and board sizes
#define TEXTURE_XUNIT 57
#define TEXTURE_YUNIT 57
#define SCALEFACTOR (g_->tilesize_x / (float) TEXTURE_XUNIT)
#define BLOCK_XSIZE (g_->tilesize_x)
#define BLOCK_YSIZE (g_->tilesize_y)
#define GRID_XSIZE 8
#define GRID_YSIZE (g_->numtiles_y)
#define PX_LEFT (g_->px_left)
#define PX_BOTTOM (g_->px_bottom)
#define PX_XSIZE (GRID_XSIZE * BLOCK_XSIZE)
#define PX_YSIZE (GRID_YSIZE * BLOCK_YSIZE)
#define PX_RIGHT (PX_LEFT + PX_XSIZE)
#define PX_TOP (PX_BOTTOM + PX_YSIZE)

// Sidebar element positions
#define SIDEBARPT(rel) (ccp(g_->sidebar_x + g_->rel.x, g_->rel.y))
#define DANGERSIGNPOS (SIDEBARPT(dangersignpos))
#define NEXTLABELPOS (SIDEBARPT(nextlabelpos))
#define TIMERPOS (SIDEBARPT(timerpos))
#define PAUSEPOS (SIDEBARPT(pausepos))
#define HELPPOS (SIDEBARPT(helppos))
#define MENUPOS (SIDEBARPT(menupos))
#define MASCOTPOS (SIDEBARPT(mascotpos))
#define BOARDPT(rel) (ccp(g_->px_left + g_->rel.x, g_->px_bottom + g_->rel.y))
#define WINLABELPOS (BOARDPT(winlabelpos))

// Trash icon positions
#define ICONXOFFSET (40 * (g_->mirrored ? 1 : -1))
#define ICONYOFFSET 24

// Mascot-relative positions
#define MASCOTSCALE (SCALEFACTOR) // (288.0 / 400.0)
#define SHADOWSCALE ((scene.layout_ == SINGLEPLAYER) ? 1.0 : ((g_->mascot == KEITH) ? 0.85 : 0.9))
#define STANDBLOCK_YPOS 304
#define CROUCHBLOCK_YPOS 207
#define MASCOTBLOCK_XPOS ((TEXTURE_XUNIT*1.5) + (g_->mirrored ? 0 : 225 - (TEXTURE_XUNIT * 3)))
#define CROUCHPOS ccp(MASCOTBLOCK_XPOS, CROUCHBLOCK_YPOS+(TEXTURE_YUNIT*0.5))
#define CURPOS ccp(MASCOTBLOCK_XPOS, STANDBLOCK_YPOS+(TEXTURE_YUNIT*0.5))
#define NEXTPOS ccp(MASCOTBLOCK_XPOS, STANDBLOCK_YPOS+100+(TEXTURE_YUNIT*0.5))

// Scrolling background parameters
#define BGSQUAREX 100
#define BGSQUAREY 100
#define SCROLLSPEED 0.5


// Stack height params
#define GOALLINE_INTERVAL 25
#define GENTREE_HEIGHT 500


// User default strings
#define D_LASTREAD @"lastRead"
#define D_NUMPLAYS @"numPlays"
#define D_ROBOTUNLOCKED @"robotUnlocked"
#define D_TIPCOUNT @"tipCount"
#define D_HEARTSCLEARED @"heartsCleared"
#define D_SPECIALCLEARED @"specialCleared"
#define D_LINESMANUALLYRAISED @"linesManuallyRaised"
#define D_ROBOTMAXSPEED @"robotMaxSpeed"
#define D_DEEPESTREACHED @"deepestReached"
#define D_COMBOCOUNT(x) ([NSString stringWithFormat:@"comboCount%d", x])
#define D_CHAINCOUNT(x) ([NSString stringWithFormat:@"chainCount%d", x])

// Chat name strings
#define C_TTBEGIN(char1, char2, num) ([NSString stringWithFormat:@"trashTalkBegin%dv%dx%d", char1, char2, num])
#define C_TTEND(char1, char2, num) ([NSString stringWithFormat:@"trashTalkEnd%dv%dx%d", char1, char2, num])
#define C_PLACEHINT @"placeHint"
#define C_RAISEHINT @"raiseHint"


// Scenemanager states
enum GameState {
	MAINMENU,
	CREDITS,
	INSTRUCTIONS,
	PLAYING,
	ENDING,
	ENDED_SOLO,
	ENDED_VSAI,
	ENDED_VSAI_COMPLETE,
	ENDED_TWOPLAYER,
};
typedef enum GameState GameState;

enum IngameState {
	UNINITIALIZED,
	MASCOTSELECT,
	RAISING,
	PLAY,
};
typedef enum IngameState IngameState;

enum GameLayout {
	SINGLEPLAYER,
	TWOPLAYERS,
	TWOPLAYERSREVERSED,
	SINGLEVSAI,
};
typedef enum GameLayout GameLayout;

@class PuzzleStack;
@class MainMenu;
@class Instructions;

// Overall game manager container, transitioning between menus and different play states.
@interface SceneManager : CCScene <CCTargetedTouchDelegate> {
	GameState state_;
	GameLayout layout_;

	MainMenu* mainmenu_;
	Instructions* instructions_;

	PuzzleStack* layer1_;
	PuzzleStack* layer2_;

	CCNode* bg_;
	CCSprite* screendim_;
	CCNode* menubox_;
	CCSprite* menuquestion_;
	CCSprite* menuyes_;
	CCSprite* menuno_;
	CCSprite* paused_;

@public
	bool memorywarning_;

	bool ispaused_;
	bool ismenudialog_;
	bool playinginterstitial_;
	bool everrotated_;

	GridConfig singleplayergrid;
	GridConfig base2playergrid;  // used as a template for 2-player grids and for PuzzleCommon only.
	GridConfig player1grid, player2grid;
	GridConfig player1grid_reversed, player2grid_reversed;

	ccDeviceOrientation bufferedPortraitOrientation_, bufferedLandscapeOrientation_;
	ccDeviceOrientation currentOrientation_;

	NSDictionary* chatdict_;

	bool mainmenumusic_;

#if !COCOSDENSHION
	AVAudioPlayer* music_;
//	AVAudioPlayer* sounds_[MAX_SOUNDS];
	NSMutableDictionary* sounddict_;
#endif
}

@property (nonatomic,readonly) GameState state_;
@property (nonatomic,readonly) GameLayout layout_;

+(id) sceneManager;
-(void) initGridConstants;
-(void) initDefaults;
-(id) init;
-(void) initChatDict;

-(void) cleanPlayfield;
-(void) changeState:(GameState)state;
-(void) changeLayout:(GameLayout)mode;

-(void) scrollBackground: (ccTime) dt;
-(void) endTimer: (ccTime) dt;

-(void) beginPause:(bool)menu isRotated:(bool)isrotated;
-(void) endPause;

-(void) receiveUserOrientation:(ccDeviceOrientation)interfaceOrientation;
-(void) forceRotation:(ccDeviceOrientation)newOrientation;

-(void) playMusic:(NSString*) music;
-(void) pauseMusic;
-(void) resumeMusic;
-(int) playSound:(NSString*) sound volume:(float)volume;
-(void) stopSound:(int) soundid;
@end

#define STARTTITLEPOS ccp(72, 768+50)
#define CROUCHEDTITLEPOS ccp(72, 463-185)
#define HELDTITLEPOS ccp(72, 463-107)
#define FINALTITLEPOS ccp(72, 768-205)

#define PREVTIPPOS ccp(350+(293/2), 768-566)
#define TIPPOS ccp(700+(293/2), 768-566)
#define NEXTTIPPOS ccp(1050+(293/2), 768-566)

#define PREVSPEEDPOS ccp(PX_LEFT + PX_XSIZE/2 - 400, 1024-493)
#define SPEEDPOS ccp(PX_LEFT + PX_XSIZE/2, 1024-493)
#define NEXTSPEEDPOS ccp(PX_LEFT + PX_XSIZE/2 + 400, 1024-493)


#define MANTOUCHSIZE ccp(166, 320)

// Main menu UI
@interface MainMenu : CCLayer {
	CCSprite* puzzle_;
	CCSprite* single_;
	CCSprite* singlevsai_;
	CCSprite* twoplayer_;
	CCSprite* twoplayerreversed_;
	CCSprite* help_;
	CCSprite* credits_;

	int currenttip_;
	int maxtip_;
	bool tipswitching_;
	CCLabel* tip_[3];
	CCSprite* tiparrows_[2];

	CCSprite* titlebeam_;
	CCSprite* mascots_;
	CCSprite* fingers_;
	CCSprite* finalchars_[2];
	CCSprite* arms_;
	CCSprite* menubg_[4];

	CCSprite* mantoucharea_;
	CCSprite* beartoucharea_;

	CCTexture2D* mascotbg_[5];
	CCTexture2D* mascotfg_[5];
	CCTexture2D* manjump_[3];
	CCTexture2D* bearjump_[3];
}

-(id) init;

-(void) initTip:(id)sender;
-(void) switchTip:(bool)prev;
-(void) finalPlacement;
-(void) mainMenuAnim;
-(void) landSound:(id)sender;

@end

@class Block;

@interface Instructions : CCLayer {
	CCSprite* page_;

	bool showcredits_;
	int pageno_;

	GameState oldstate_;

	GridConfig tutorialgrid_;

	CCSprite* nextbutton_;
	CCSprite* exitbutton_;

	CCLabel* label1_;
   	CCLabel* label2_;

	CCNode* placebox_;
	Block* tutorialblock_[4];
	Block* leftblock_;
	Block* rightblock_;
	Block* upblock_;
	Block* downblock_;

	CCNode* fallmatchbox_;
	Block* falltutorial_;
	Block* fallblock_;
	Block* matchblock_;

	CCNode* combobox_;
	Block* combotutorial_;
	Block* place_;
	Block* double_;
	Block* single_;

	CCNode* chainbox_;
	Block* chaintutorial_;
	Block* vertical_[2];
	Block* chain1_[2];
	Block* chain2_[2];
}

-(id) init:(GameState)oldstate showCredits:(bool)showcredits;
- (void)updatePage;

- (void)removeAnimations:(id)sender;
- (void)placeAnimation:(id)sender;
- (void)fallMatchAnimation:(id)sender;
- (void)comboAnimation:(id)sender;
- (void)comboCircle:(id)sender;
- (void)chainAnimation:(id)sender;
- (void)chainStar1:(id)sender;
- (void)chainStar2:(id)sender;
@end

enum BlockColor;

// Game state shared between both players (so that the game is fair by giving them both the same stack and next-blocks)
@interface PuzzleCommon : NSObject {
	// g_ used here only for shared basics like board size in tiles
	GridConfig* g_;

	// Block colors to be placed next.  We keep track of all of them in an array from the beginning.
	NSMutableArray* blockcolors_;
	NSMutableArray* blockdoubleness_;

	// Tree of blocks rising from below
	NSMutableArray* blocktree_;
	@public
	// Lowest level tree has been populated to.
	int treedepth_;
}

-(id) initWithGridConfig:(GridConfig*) grid;
-(BlockColor) getBlockColorToPlace:(int) count;
-(bool) getBlockDoublenessToPlace:(int) count;
-(void) getBlocksAtRaiseLevel:(int)raiselevel blockarray:(Block**)blockarray;

-(void) generateRandomTreeWithDepth:(int)depth startheight:(int)startheight;
-(void) loadPredefinedLevel:(const char*) level startheight:(int)startheight;

@end

